package Risk.Moteur;

import Risk.IHM.PlateauIhm;

import Risk.Reseau.TraitementClient;

import java.awt.Color;

import java.io.File;
import java.io.Serializable;

import java.util.ArrayList;

/**
 *  Jeu virtuel (classe principale)
 *     @version    1.0, 07/12/26
 *   @author     Alexandre LIGUORI
 */
public class Jeu implements Serializable {

    /**
     * phase attaque
     */
    public static final int	ATTAQUE = 1;

    /**
     * type de la partie (fin de la partie quand
     * un joueur possede tous les pays du plateau)
     */
    public static final int	CONQUERIR_LE_MONDE = 20;

    /**
     * erreur
     */
    public static final int	ERREUR = -10;

    /**
     * fin de partie
     */
    public static final int	FIN_PARTIE = 10;

    /**
     * Phase d'initialisation (placement des armees)
     */
    public static final int	INITIALISATION = -1;

    /**
     * phase Maneuvre
     */
    public static final int	MANOEUVRE = 2;

    /**
     * type de partie (sans fin)
     */
    public static final int	MISSIONS_SECRETES = 21;

    /**
     * la partie continue
     */
    public static final int	PARTIE_CONTINUE = 11;

    /**
     * phase renfort
     */
    public static final int	RENFORT = 0;

    // Sebastien : Ajout d'un champ pour securiser la serialisation
    private static final long		serialVersionUID = 16L;
    private int				joueurCourant;
    private ArrayList<Joueur>		joueurs;
    private int				phase;
    private transient PlateauIhm	pihm;
    private PileCartes			pileCartes, defausse;
    private Plateau			plateau;
    private transient TraitementClient	tc;
    private int				typePartie;

    /**
     * Nouveau jeu (initialise toute la partie virtuelle du risk)
     */
    public Jeu() {
	this.joueurs = new ArrayList<Joueur>();
	this.plateau = new Plateau(this);
	plateau.initialisationContinents("donnee" + File.separator + "renfort_continents.prn");
	plateau.intialiseFrontiere("donnee" + File.separator + "voisins.prn");
	this.pileCartes = new PileCartes();
	this.defausse   = new PileCartes();
	pileCartes.initialisePile(plateau);
	this.joueurCourant = 0;
	this.typePartie    = CONQUERIR_LE_MONDE;    // par defaut
	tc                 = null;
	this.pihm          = null;
	this.phase         = INITIALISATION;
    }

    /**
     * Ajoute un joueur
     * @param val un nouveau joueur
     * @return true si ok, false sinon
     */
    private boolean ajouteJoueur(Joueur val) {
	for (Joueur j : joueurs) {
	    if (j.getNom().equals(val.getNom())) {
		System.out.println("Erreur dans ajouteJoueur : nom du nouveau joueur déjà existant");

		return false;
	    }
	}

	return this.joueurs.add(val);
    }

    /**
     * Ajoute un joueur
     * @param nom nom du joueur
     * @param couleur couleur du joueur
     * @return true si ok, false sinon
     */
    public boolean ajouteJoueur(String nom, Color couleur) {
	Joueur	j = new Joueur(nom, couleur);

	return ajouteJoueur(j);
    }

    /**
     * depense (apres avoir tester) une combinaison de carte +
     * verification si une des cartes correspond a un pays du joueur courant (dans ce cas, +2 armees)
     * @param indice tableau de la position des cartes en main choisi (nombre de carte = 3)
     * @return true si reussi, false sinon
     */
    public boolean depenseCarteJoueurCourant(int[] indice) {
	int	retour;

	retour = joueurs.get(joueurCourant).testDepenseCartes(indice);

	if (Joueur.ERREUR != retour) {

	    // ajoute des armees a la reserve du joueur
	    joueurs.get(joueurCourant).setReserveArmee(retour + joueurs.get(joueurCourant).getReserveArmee());

	    // remet les cartes dans la defausse
	    for (int i = 0; i < indice.length; i++) {
		defausse.ajouteCarte(getCartesJoueurCourant().get(indice[ i ]));
	    }

	    // retire les cartes utilisees
	    joueurs.get(joueurCourant).retireCartesEnMain(indice);

	    return true;
	} else {
	    System.out.println("Erreur dans depenseCarteJoueurCourant");

	    return false;
	}
    }

    /**
     * Test si un joueur j est mort
     * @param j le joueur a teste
     * @return True si mort, false sinon
     */
    public boolean estMort(Joueur j) {
	for (Continent conti : plateau.getContinents()) {
	    for (Pays p : conti.getPaysContinent()) {
		if (p.getJoueur().getNom().equals(j.getNom())) {
		    return false;
		}
	    }
	}

	return true;
    }

    /**
     * test si un joueur peut encore jouer
     * @return false si le joueur peut continuer la partie, true sinon
     */
    public boolean estMortJoueurCourant() {
	for (Continent conti : plateau.getContinents()) {
	    for (Pays p : conti.getPaysContinent()) {
		if (p.getJoueur().getNom().equals(joueurs.get(joueurCourant).getNom())) {
		    return false;
		}
	    }
	}

	return true;
    }

    /**
     * affiche etat plateau
     */
    public void etatPlateau() {
	for (Continent conti : plateau.getContinents()) {
	    for (Pays p : conti.getPaysContinent()) {
		System.out.println(p.getNom() + " appartient à " + p.getJoueur() + " avec " + p.getNbArmee()
				   + " soldat(s)");
	    }
	}
    }

    /**
     * incremente le compteur de phase
     * @return le compteur de phase
     */
    private int incrementePhase() {
	phase = (phase + 1) % 3;

	return phase;
    }

    /**
     * - compte les reserves armees de chaque joueur
     * - distribution aleatoirement les territoires
     * - met automatiquement 1 armees du joueurs en question sur chaque territoire
     */
    public void initialiseDebutPartie() {
	int	nbArmeeJoueur = 40;
	Carte	carte_TMP     = null;

	// nb armee en fct du nb de joueurs
	for (int nbJoueur = 2; nbJoueur < joueurs.size(); nbJoueur++) {
	    nbArmeeJoueur = nbArmeeJoueur - 5;
	}

	System.out.println("nbArmee par joueur=" + nbArmeeJoueur);

	// attribue en reserve de chaque joueur
	for (Joueur j : joueurs) {
	    j.setReserveArmee(nbArmeeJoueur);
	}

	// distribution aleatoire des territoires
	pileCartes.melange();

	while (!pileCartes.estVide()) {
	    carte_TMP = pileCartes.tireCarte();
	    carte_TMP.getPays().setJoueur(joueurs.get(joueurCourant));

	    // mettre directement 1 armee par teritoire
	    carte_TMP.getPays().ajouteArmee(joueurs.get(joueurCourant).decrementeReserveArmee());
	    joueurSuivant();
	}

	for (Joueur j : joueurs) {
	    System.out.println(j.getNom() + " possede " + j.getReserveArmee() + " soldats / "
			       + plateau.getNbPaysParJoueur(j) + " pays");
	}

	joueurCourant = 0;

	// la pile de carte est vide, donc reinitialisation (avec melange)
	reinitPileCartes();
    }

    /**
     * Sert à simuler des jet de des (pour attaque)
     * @param nbDe nombre de de
     * @param p pays attaquant (pour verifier la validite du nombre de de)
     * @return une liste d integer representant les jets effectue
     */
    public static ArrayList<Integer> jetAttaquant(int nbDe, Pays p) {
	return Pays.jetAttaquant(nbDe, p);
    }

    /**
     * Sert à simuler des jet de des (pour defense)
     * @param nbDe nombre de de
     * @param p pays defenseur (pour verifier la validite du nombre de de)
     * @return une liste d integer representant les jets effectue
     */
    public static ArrayList<Integer> jetDefenseur(int nbDe, Pays p) {
	return Pays.jetDefenseur(nbDe, p);
    }

    /**
     * le joueur attaque un pays (defenseur) avec un de ces pays (attaquant),
     * les jets de des doivent etre passe en parametre (utilisez jetAttaquant et jetDefenseur dans Jeu)
     * @param attaquant pays attaquant
     * @param defenseur pays defenseur
     * @param jetAttaquant jet de des de l attaquant
     * @param jetDefenseur jet de des du defenseur
     * @return code a tester avec varaible static de la classe (soit PARTIE_CONTINUE ou FIN_PARTIE ou ERREUR)
     */
    public int joueurCourantAttaque(Pays attaquant, Pays defenseur, ArrayList<Integer> jetAttaquant,
				    ArrayList<Integer> jetDefenseur) {
	int	retour;

	// verification que joueur courant possede bien le pays attaquant
	if (!attaquant.controlePays(joueurs.get(joueurCourant))) {
	    System.out.println("Erreur dans joueurCourantAttaque : joueur courant ne controle pas "
			       + attaquant.getNom());

	    return ERREUR;
	}

	// verification que joueur courant possede bien le pays defenseur
	if (defenseur.controlePays(joueurs.get(joueurCourant))) {
	    System.out.println("Erreur dans joueurCourantAttaque : joueur courant controle deja " + defenseur.getNom());

	    return ERREUR;
	}

	// attaque
	retour = attaquant.attaque(defenseur, jetAttaquant, jetDefenseur);

	// traitement du retour de la methode attaque
	if (retour == Pays.ERREUR) {              // cas erreur
	    System.out.println("Erreur dans joueurCourantAttaque");

	    return ERREUR;
	} else if (retour == Pays.VAINQUEUR) {    // cas prise du controle du pays defenseur
	    if (isFinPartie()) {                  // cas fin de partie (si le defenseur etait le dernier pays
		return FIN_PARTIE;
	    }

	    // joueur courant tire une carte bonus
	    if (!pileCartesEstVide()) {
		joueurs.get(joueurCourant).ajouteCarteBonus(pileCartes.tireCarte());
	    } else {
		remplacePileCartes();

		if (!pileCartesEstVide()) {
		    joueurs.get(joueurCourant).ajouteCarteBonus(pileCartes.tireCarte());
		} else {
		    System.out.println("Erreur dans joueurCourantAttaque : La pioche de carte est vide.");

		    return ERREUR;
		}
	    }
	}

	return retour;
    }

    /**
     * Test si un joueur possede tous les pays
     * @return True si un joueur possede tous les pays, false sinon
     */
    private boolean joueurCourantPossedeTousLesPays() {
	for (Continent conti : plateau.getContinents()) {
	    for (Pays p : conti.getPaysContinent()) {
		if (!p.getJoueur().getNom().equals(joueurs.get(joueurCourant).getNom())) {
		    return false;
		}
	    }
	}

	return true;
    }

    /**
     * remplace le joueur courant par le suivant
     */
    public void joueurSuivant() {
	if ((joueurCourant + 1) < joueurs.size()) {
	    joueurCourant++;
	} else {
	    joueurCourant = 0;
	}
    }

    /**
     * Test si les joueurs possede encore des armees en reserve
     * (utilise en phase d initialisation)
     * @return True si au moins un joueur possede une armee en reserve, false sinon
     */
    public boolean joueursPossedeReserve() {
	for (Joueur j : joueurs) {
	    if (j.getReserveArmee() != 0) {
		return true;
	    }
	}

	return false;
    }

    /**
     * un joueur deplace des armees d un de ces pays vers un autre
     * @param depart pays de depart
     * @param arrive pays d arrive
     * @param nbArmee nombre d armee deplace
     * @return true si reussi, false sinon
     */
    public boolean manoeuvreJoueurCourant(Pays depart, Pays arrive, int nbArmee) {

	// verification que joueur courant possede bien le pays attaquant
	if (!depart.controlePays(joueurs.get(joueurCourant))) {
	    System.out.println("Erreur dans manoeuvreJoueurCourant : joueur courant ne controle pas "
			       + depart.getNom());

	    return false;
	}

	// verification que joueur courant possede bien le pays defenseur
	if (!arrive.controlePays(joueurs.get(joueurCourant))) {
	    System.out.println("Erreur dans manoeuvreJoueurCourant : joueur courant ne controle pas "
			       + arrive.getNom());

	    return false;
	}

	if (!depart.deplaceArmeeVers(arrive, nbArmee)) {
	    System.out.println("Erreur dans manoeuvreJoueurCourant");

	    return false;
	}

	return true;
    }

    /**
     * Sert a passe a la phase suivante
     * (Gestion interne du passage au joueur suivant)
     */
    public void phaseSuivante() {
	pihm.getJbTirDes().setEnabled(false);
	pihm.getImgCarte().effaceSelection();

	if (phase != INITIALISATION) {
	    incrementePhase();
	}

	System.out.println("n° phase=" + phase);

	switch (phase) {
	case INITIALISATION :
	    if (!joueursPossedeReserve()) {            // fin initialisation
		phase         = MANOEUVRE;             // a la prochaine phase ==> renfort
		joueurCourant = joueurs.size() - 1;    // au prochain tour ==> 1 er joueur
		phaseSuivante();
	    } else {
		do {
		    joueurSuivant();
		} while (getJoueurCourant().getReserveArmee() == 0);

		pihm.panelJoueurCourant();
		pihm.modifierTexte("Phase initialisation " + getJoueurCourant() + "\n"
				   + "Cliquez sur un pays pour y placer un soldat");
	    }

	    break;

	case RENFORT :
	    do {
		joueurSuivant();
	    } while (estMortJoueurCourant());    // saute les joueurs morts

	    renfortPourJoueurCourant();
	    pihm.panelJoueurCourant();
	    pihm.modifierTexte("Phase renfort " + getJoueurCourant() + "\nPlacez vos soldats");

	    break;

	case ATTAQUE :
	    pihm.modifierTexte("Phase attaque " + getJoueurCourant() + "\nChoisissez le pays attaquant");

	    break;

	case MANOEUVRE :
	    pihm.modifierTexte("Phase manoeuvre " + getJoueurCourant() + "\nChoisissez le pays de depart");

	    break;

	default :
	    System.out.println("Erreur dans phase suivante");

	    break;
	}

	pihm.getImgCarte().repaint();
    }

    /**
     * Test si la pile de cartes est vide
     * @return true si ok, false sinon
     */
    public boolean pileCartesEstVide() {
	return pileCartes.estVide();
    }

    /**
     * reinitialisation de la pile de carte (avec melange)
     */
    public void reinitPileCartes() {
	pileCartes.initialisePile(plateau);
	pileCartes.melange();
    }

    /**
     * reinitialise a 0 la reserve du joueur
     */
    public void reinitReserveJoueurCourant() {
	joueurs.get(joueurCourant).setReserveArmee(0);
    }

    /**
     * remet les cartes de la defausse dans la pioche
     */
    public void remplacePileCartes() {
	pileCartes.ajouteCartes(defausse.getPileDeCartes());
	pileCartes.melange();
	defausse = new PileCartes();
    }

    /**
     * calcul les renforts pour le joueur
     * @param j le joueur
     * @return le nombre de renfort
     */
    private int renfortPourJoueur(Joueur j) {
	int	renfort = 3;

	// nbPays
	if ((plateau.getNbPaysParJoueur(j) / 3) > renfort) {
	    renfort = (plateau.getNbPaysParJoueur(j) / 3);
	}

	// continent
	renfort += plateau.renfortContinentPourJoueur(j);

	return renfort;
    }

    /**
     * effectue les calculs de renfort pour augmenter la reserve des soldat a placer
     * @return le nombre de soldats en reserve
     */
    public int renfortPourJoueurCourant() {
	joueurs.get(joueurCourant).setReserveArmee(renfortPourJoueur(joueurs.get(joueurCourant)));

	return getReserveJoueurCourant();
    }

    /**
     * Test si le joueur peut terminer sa phase renfort, soit :
     * S il n a pas trop de carte en main (moins de 5)
     * S il n a plus de soldat en reserve
     * @return True si les deux conditions sont respecte, false sinon
     */
    public boolean testFinRenfort() {
	if (phase == RENFORT) {
	    if (tropCartesJoueurCourant()) {
		pihm.modifierTexte("Vous avez trop de cartes en main !");

		return false;
	    }

	    if (getReserveJoueurCourant() != 0) {
		pihm.modifierTexte("Vous devez depenser toutes vos armees en reserve");

		return false;
	    }
	}

	return true;
    }

    /**
     * test si un joueur a trop de cartes en main (>5)
     * @return true s il a trop de carte, faux sinon
     */
    public boolean tropCartesJoueurCourant() {
	return joueurs.get(joueurCourant).tropDeCartesEnMain();
    }

    /**
     * renvoie les cartes en main du joueur
     * @return liste des cartes en main
     */
    public ArrayList<Carte> getCartesJoueurCourant() {
	return joueurs.get(joueurCourant).getCartesEnMain();
    }

    /**
     * revoie la defausse de carte
     * @return defausse de carte
     */
    public PileCartes getDefausse() {
	return defausse;
    }

    /**
     * renvoie le joueur courant
     * @return joueur courant
     */
    public Joueur getJoueurCourant() {
	return joueurs.get(joueurCourant);
    }

    /**
     * renvoie la liste des joueurs
     * @return liste des joueurs
     */
    public ArrayList<Joueur> getJoueurs() {
	return joueurs;
    }

    /**
     * renvoie l'objet pays, selon en parametre
     * @param Pays nom du pays recherche
     * @return l'objet pays
     */
    public Pays getPays(String Pays) {
	return plateau.getPays(Pays);
    }

    /**
     * revoie la phase courante
     * @return la phase courante
     */
    public int getPhase() {
	return phase;
    }

    /**
     * Renvoie le plateau IHM
     * @return le plateau IHM
     */
    public PlateauIhm getPihm() {
	return pihm;
    }

    /**
     * renvoie la pile de carte
     * @return pile de carte
     */
    public PileCartes getPileCartes() {
	return pileCartes;
    }

    /**
     * renvoie le plateau
     * @return plateau
     */
    public Plateau getPlateau() {
	return plateau;
    }

    /**
     * renvoie le nombre de soldat a placer sur le plateau (reserve)
     * @return le nombre de soldats en reserve
     */
    public int getReserveJoueurCourant() {
	return joueurs.get(joueurCourant).getReserveArmee();
    }

    /**
     * Renvoie l objet de traitement client
     * @return le traitement client
     */
    public TraitementClient getTc() {
	return tc;
    }

    /**
     * renvoie le type de partie (en int, voir les variables statiques
     * @return type de la partie
     */
    public int getTypePartie() {
	return typePartie;
    }

    /**
     * Test si fin de partie (ne marche pas en mode MISSIONS_SECRETES)
     * @return true si un joueur possede tous les pays, false sinon
     */
    public boolean isFinPartie() {
	if (typePartie == MISSIONS_SECRETES) {
	    return false;
	} else if (joueurCourantPossedeTousLesPays()) {
	    return true;
	} else {
	    return false;
	}
    }

    /**
     * setter du joueur courant
     * @param joueurCourant indice du joueur courant
     */
    public void setJoueurCourant(int joueurCourant) {
	this.joueurCourant = joueurCourant;
    }

    /**
     * setter du plateau ihm
     * @param pihm le plateau ihm
     */
    public void setPihm(PlateauIhm pihm) {
	this.pihm = pihm;
    }

    /**
     * setter de traitement connection
     * @param tc traitement connection
     */
    public void setTc(TraitementClient tc) {
	this.tc = tc;
	TraitementClient.setIntCompteur(0);
    }

    /**
     * changer le type de partie (Conquerir le monde, par defaut)
     * @param typePartie type de partie (en int)
     */
    public void setTypePartie(int typePartie) {
	this.typePartie = typePartie;
    }
}
